import serial
import time
import os
import typing
import threading


class UartLib(object):
    USB_TYPE_2 = 2
    USB_TYPE_3 = 3
    
    USB_PORT_1 = 1
    USB_PORT_2 = 2
    USB_PORT_3 = 3
    USB_PORT_4 = 4
    
    CAN_PORT_1 = 1
    CAN_PORT_2 = 2
    
    CAN_CYCLE_10MS = 1
    CAN_CYCLE_20MS = 2
    CAN_CYCLE_50MS = 3
    CAN_CYCLE_100MS = 4
    CAN_CYCLE_200MS = 5
    CAN_CYCLE_500MS = 6
    
    def __init__(
        self, 
        port:str = '/dev/ttyUSB0',
        baudrate:int = 115200) -> None:
        self._serial = serial.Serial(
            port=port,
            baudrate=baudrate,
        )
        
        threading.Thread(
            target=self._ThreadEchoUartData,
            daemon=True
        ).start()
    
    def UsbConfig(self, type: int, port: int, onoff: bool) -> None:
        '''
        控制一路 USB 使能
        '''
        # 开关 byte
        switch_byte_ = ''
        if (onoff):
            switch_byte_ = '01'
        else:
            switch_byte_ = '00'
        
        # USB byte
        bus_byte_ = ''
        if (type == UartLib.USB_TYPE_2):
            if (port == UartLib.USB_PORT_1):
                bus_byte_ = '01'
            elif (port == UartLib.USB_PORT_2):
                bus_byte_ = '02'
            elif (port == UartLib.USB_PORT_3):
                bus_byte_ = '04'
            elif (port == UartLib.USB_PORT_4):
                bus_byte_ = '08'
        if (type == UartLib.USB_TYPE_3):
            if (port == UartLib.USB_PORT_1):
                bus_byte_ = '10'
            elif (port == UartLib.USB_PORT_2):
                bus_byte_ = '20'
            elif (port == UartLib.USB_PORT_3):
                bus_byte_ = '40'
            elif (port == UartLib.USB_PORT_4):
                bus_byte_ = '80'
        d = bytes.fromhex('5A 01 ' + switch_byte_ + ' ' + bus_byte_ + ' 0D')
        res_ = self._serial.write(d)
        print('UsbConfig result = {0}'.format(res_))
    
    def Usb3ConfigAll(self, onoff: bool) -> None:
        '''
        控制所有 USB3 使能
        '''
        d = bytes.fromhex('5A 01 FF F0 0D')
        res_ = self._serial.write(d)
        print('Usb3ConfigAll result = {0}'.format(res_))
    
    def Usb2ConfigAll(self, onoff: bool) -> None:
        '''
        控制所有 USB2 使能
        '''
        d = bytes.fromhex('5A 01 FF 0F 0D')
        res_ = self._serial.write(d)
        print('Usb2ConfigAll result = {0}'.format(res_))
        
    def RelyConfig(self, onoff: bool) -> None:
        '''
        控制继电器开关
        '''
        d = bytes.fromhex('5A 01 FF FF 0D')
        res_ = self._serial.write(d)
        print('RelyConfig result = {0}'.format(res_))
    
    def CanMsgConfig(self, can_port:int, can_id:int, onoff: bool, cycle:int, length:int, data:typing.List[int]) -> None:
        '''
        自定义CAN发送
        '''
        head_ = '5A 03'
        tail_ = '06'
        port_ = ''
        id_ = ''
        config_ = ''
        data_length_ = ''
        data_ = ''
        
        # 端口
        if (can_port == UartLib.CAN_PORT_1):
            port_ = '01'
        elif (can_port == UartLib.CAN_PORT_2):
            port_ = '02'
        else:
            print('CanMsgConfig 不支持的 can_port')
            return
        
        # id
        can_id = can_id & 0xFFFFFFFF
        id_ = '{:0>8}'.format(hex(can_id)[2:])
        
        # 配置
        if (cycle == UartLib.CAN_CYCLE_10MS):
            config_ = '01'
        elif (cycle == UartLib.CAN_CYCLE_20MS):
            config_ = '02'
        elif (cycle == UartLib.CAN_CYCLE_50MS):
            config_ = '03'
        elif (cycle == UartLib.CAN_CYCLE_100MS):
            config_ = '04'
        elif (cycle == UartLib.CAN_CYCLE_200MS):
            config_ = '05'
        elif (cycle == UartLib.CAN_CYCLE_500MS):
            config_ = '06'
        
        # 长度
        length = length & 0xFF
        data_length_ = hex(length)[2:]
        
        # 数据
        if (length != len(data)):
            print('CanMsgConfig 数据长度和实际数据长度不一致, 输入长度 {0}， 实际长度 {1}'.format(length, len(data)))
            return
        for i_ in data:
            data_ += hex(i_ & 0xFF)[2:]
        
        s_ = head_ + port_ + id_ + config_ + data_length_ + data_ + tail_
        d = bytes.fromhex(s_)
        res_ = self._serial.write(d)
        print('CanMsgConfig result = {0}'.format(res_))
    
    def CanMsgEcho(self, can_port:int, can_id:int) -> None:
        '''
        打印 CAN 数据
        # TODO PPT中未规定此消息格式
        '''
        head_ = '5A 03'
        tail_ = '06'
        port_ = ''
        id_ = ''
        
        # 端口
        if (can_port == UartLib.CAN_PORT_1):
            port_ = '01'
        elif (can_port == UartLib.CAN_PORT_2):
            port_ = '02'
        else:
            print('CanMsgConfig 不支持的 can_port')
            return
        
        # id
        can_id = can_id & 0xFFFFFFFF
        id_ = '{:0>8}'.format(hex(can_id)[2:])
        
        d = bytes.fromhex(head_ + port_ + id_ + tail_)
        res_ = self._serial.write(d)
        print('CanMsgEcho result = {0}'.format(res_))
    
    def _ThreadEchoUartData(self) -> None:
        '''
        线程函数，定时读取数据并打印
        # TODO 未规定串口返回数据的格式，即不好实现同步收发，只能先开线程实现异步收发
        '''
        while (True):
            time.sleep(0.2)
            b_data_ = self._serial.read_all()
            if (not b_data_):
                continue
            print('UartLib.READ: ', ''.join(['%02X ' % b for b in b_data_]))
            
    